package com.aldobucchi.ds.op
{

	import flash.utils.Dictionary;
	
	import com.aldobucchi.ds.DataStructureEvent;
	import com.aldobucchi.ds.container.ContainerDiff;
	import com.aldobucchi.ds.container.IOContainer;
	import com.aldobucchi.ds.container.IRContainer;
	import com.aldobucchi.ds.container.bag.IWBag;
	import com.aldobucchi.ds.container.utils.ContainerElementsListenerHelper;
	import com.aldobucchi.ds.container.utils.MultiplicityChange;
	import com.aldobucchi.x.flash.events.EventListenerSpec;

	public class ContainerUnionOp extends AbstractContainerToBagOp
	{
		

		/**
		 * Utility to automatically subscribe to events from child bags
		 * 
		 */		
		private var containerElementsListenerHelper:ContainerElementsListenerHelper;		
		
		
		
		public function ContainerUnionOp( iContainer:IRContainer, oBag:IWBag )
		{
			
			super( iContainer, oBag );
			
			
			if ( iContainer is IOContainer )
			{

				containerElementsListenerHelper = new ContainerElementsListenerHelper( [ 
					new EventListenerSpec( DataStructureEvent.CHANGE, iContainer_elements_change )
					] );
					
				containerElementsListenerHelper.container = iContainer as IOContainer;
						
			}
			
			
			batch( );
			
		}
		
		
		
		
		
		private function batch( ):void
		{
			
			oBag.clear( );
		
			// first copy... iterate over all the input containers
			// and add their elements
			iContainer.walk(
				function( e:*, multiplicity:int ):void
				{
					if ( e is IRContainer )
					{
						addChildContainer( e as IRContainer, multiplicity );
					}												
				}
			);
		
		}		
		
		
		
		
		/**
		 * 
		 * @param container
		 * @param add
		 * @param multiplicity the number of times to add/remove the elements in this container
		 * 
		 */		
		private function addChildContainer( container:IRContainer, containerMultiplicity:int ):void
		{
			
			var ob:IWBag = oBag;
			
			container.walk(
				function( e:*, multiplicity:int ):void
				{
					// we multiply the element's multiplicity by the container's multiplicity	
					ob.add( e, containerMultiplicity * multiplicity );
				}
			);

		}
		
		
		
		
		
		/**
		 * 
		 * called when the parent container changes.
		 * 
		 * @param event
		 * 
		 */		
		override protected function iContainer_change( event:DataStructureEvent ):void
		{
		
			var diff:ContainerDiff = event.diff as ContainerDiff;
			
			var mcs:Dictionary = diff.multiplicityChanges;
			
			for ( var e:* in mcs )
			{
				
				if ( e is IRContainer )
				{
					var mc:MultiplicityChange = mcs[e] as MultiplicityChange;
					
					addChildContainer( e as IRContainer, mc.delta );			
				}
				
			}	
				
		}
		





		/**
		 * 
		 * called when a child container changes
		 * 
		 * @param event
		 * 
		 */	
		private function iContainer_elements_change( event:DataStructureEvent ):void
		{
			
			var diff:ContainerDiff = event.diff as ContainerDiff;
			var mcs:Dictionary = diff.multiplicityChanges;
			var ob:IWBag = oBag;			
			
			for ( var e:* in mcs )
			{
				var mc:MultiplicityChange = mcs[e] as MultiplicityChange;
				ob.add( e, mc.delta );				
			}			

		}
				
		
		
		
		override public function dispose():void
		{

			super.dispose();

			containerElementsListenerHelper.container = null;

		}
		
		
		
		
	}
}